﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _15Puzzle_GUI
{
    public class FifteenPuzzle
    {
        #region Properties
        private int[,] board = new int[,] { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 }, { 13, 14, 0, 15 } };
        public int FreeX { get; set; }
        public int FreeY { get; set; }
        public int Moves { get; set; }
        public int TileHeight { get; set; }
        public int TileWidth { get; set; }
        public bool Complete
        {
            get { return GameComplete(); }
        } 
        #endregion
        
        public FifteenPuzzle()
        {
            GenereateBoard();
        }

        public Rectangle MoveTiles(int xStep, int yStep)
        {
            Rectangle rect = new Rectangle((FreeX + yStep) * TileWidth, (FreeY + xStep) * TileHeight, 
                TileWidth + Math.Abs(yStep) * TileWidth, TileHeight + Math.Abs(xStep) * TileHeight);

            board[FreeX, FreeY] = board[FreeX + xStep, FreeY + yStep];
            FreeX += xStep;
            FreeY += yStep;
            board[FreeX, FreeY] = 0;
            Moves++;
            return rect;
        }

        public void DrawBoard(Graphics g)
        {
            Brush brushBackground = new SolidBrush(Color.Bisque);
            Brush brushBrick = new SolidBrush(Color.Azure);
            Pen penBorder = new Pen(new SolidBrush(Color.BlueViolet));
            Brush brushFont = Brushes.Black;
            Font font = new Font(FontFamily.GenericSansSerif, 34);

            Rectangle rect = new Rectangle(5, 5, TileWidth - 10, TileHeight - 10);

            g.FillRectangle(brushBackground, g.VisibleClipBounds);
            
            for (int i = 0; i < board.GetLength(0); i++)
            {
                rect.Y = 5;
                for (int j = 0; j < board.GetLength(1); j++)
                {                    
                    if (board[j,i] != 0)
                    {
                        g.DrawRectangle(penBorder, rect);
                        g.FillRectangle(brushBrick, rect);
                        g.DrawString(board[j,i].ToString(), font, brushFont, i * TileWidth, j * TileHeight); 
                    }
                    rect.Y += TileHeight;
                }
                rect.X += TileWidth;
            }
        }

        private bool GameComplete()
        {
            int k = 1;
            for (int i = 0; i < board.GetLength(0); i++)
            {
                for (int j = 0; j < board.GetLength(1); j++)
                {
                    if (board[i, j] != k)
                    {
                        return false;
                    }
                    k = ++k % 16;
                }
            }
            return true;
        }

        public void GenereateBoard()
        {
            Random rng = new Random();
            int[] nums = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};

            //Randomize the board until a solvable board is generated
            do
            {
                for (int i = 0; i < nums.Length; i += 1)
                {
                    int swapIndex = rng.Next(nums.Length);
                    if (swapIndex != i)
                    {
                        int tempint = nums[i];
                        nums[i] = nums[swapIndex];
                        nums[swapIndex] = tempint;
                    }
                }
            } while (!CheckSolvable(ref nums));

            //Input the generated board to the game board
            int k = 0;
            for (int i = 0; i < board.GetLength(0); i++)
            {
                for (int j = 0; j < board.GetLength(1); j++)
                {
                    board[i, j] = nums[k];
                    k++;
                }
            }

            //Find the empty space 
            for (int i = 0; i < board.GetLength(0); i++)
            {
                for (int j = 0; j < board.GetLength(1); j++)
                {
                    if (board[i, j] == 0)
                    {
                        FreeX = i;
                        FreeY = j;
                    }
                }
            }
            Moves = 0;
        }

        private bool CheckSolvable(ref int[] iref)
        {
            int inversions = 0, emptyRow = 0;
	        for (int i = 0; i < iref.Length; i++)
	        {
		        if (iref[i] == 0)
		        {
			        if ((i > 3 && i < 8) || i > 11)
			        {
				        emptyRow = 1;
			        }
		        }
		        else
		        {
			        for (int j = i + 1; j < iref.Length; j++)
			        {
				        if (iref[i] > iref[j] && iref[j] != 0){
					        inversions++;
				        }
			        }
		        }
	        }
	        if (((inversions + emptyRow) % 2) == 1)
	        {
		        return true;
	        }
	        return false;
        }
    }
}
